﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using dragonangel.phpProject.Language.DSL;
using Babel.ParserGenerator;
using Microsoft.VisualStudio.TextManager.Interop;
using Babel.Parser;

namespace dragonangel.phpProject.Language.DSL.Parser
{
    public partial class Parser : ShiftReduceParser<LexValue, LexLocation>
    {
        private CodeFile codeFile = new CodeFile();
        public CodeFile CodeFile
        {
            get { return codeFile; }
        }
        private Stack<CodeExpression> expressionStack = new Stack<CodeExpression>();
        ErrorHandler handler = null;
        public void SetHandler(ErrorHandler hdlr) { handler = hdlr; }
        internal void CallHdlr(string msg, LexLocation val)
        {
            handler.AddError(msg, val.sLin, val.sCol, val.eCol - val.sCol);
        }
        internal TextSpan MkTSpan(LexLocation s) { return TextSpan(s.sLin, s.sCol, s.eLin, s.eCol); }

        internal void Match(LexLocation lh, LexLocation rh)
        {
            DefineMatch(MkTSpan(lh), MkTSpan(rh));
        }

        public IList<Tokens> GetExpectedTokens()
        {
            IList<Tokens> tokens = new List<Tokens>();
            if (expectedTokens != null)
            {
                foreach (int token in expectedTokens)
                    tokens.Add((Tokens)token);
            }
            return tokens;
        }

        public void GetExpectedTokens(TextSpan span)
        {
            expectedTokensLocation = new LexLocation(span.iStartLine, span.iStartIndex, span.iEndLine, span.iEndIndex);
        }

        public void GetExpectedTokens(int line, int col)
        {
            expectedTokensLocation = new LexLocation(line, col, line, col);
        }

        internal void PushCodeClass(LexValue value, LexLocation begin, LexLocation end)
        {
            WriteTrace("Creating Class " + value.str, "Parser");
            CodeClass codeClass = new CodeClass(value.str, begin, end);
            expressionStack.Push(codeClass);
        }

        internal void UpdateCodeClass(LexLocation end)
        {
            if (expressionStack.Count > 0)
            {
                IList<CodeMember> members = new List<CodeMember>();
                while (expressionStack.Count > 0 && (expressionStack.Peek() is CodeMember || expressionStack.Peek() is CodeVariableDeclarationStatement))
                {
                    if (expressionStack.Peek() is CodeVariableDeclarationStatement)
                    {
                        CodeVariableDeclarationStatement vds = expressionStack.Pop() as CodeVariableDeclarationStatement;
                        CodeField field = new CodeField(vds.Name, vds.Begin, vds.End);
                        expressionStack.Push(field);
                    }
                    members.Add((CodeMember)expressionStack.Pop());
                }
                if (expressionStack.Peek() is CodeClass && members.Count > 0)
                {
                    CodeClass codeClass = expressionStack.Peek() as CodeClass;
                    codeClass.Members = members;
                    codeClass.End = end;
                }
            }
        }

        internal void UpdateBaseType(string baseType)
        {
            WriteTrace("Extends " + baseType, "Parser");
            if (expressionStack.Count > 0 && expressionStack.Peek() is CodeClass)
            {
                CodeClass codeClass = expressionStack.Peek() as CodeClass;
                codeClass.BaseType = baseType;
            }
        }

        internal void UpdateInterfaces(string iface)
        {
            WriteTrace("Implements " + iface, "Parser");
            if (expressionStack.Count > 0 && expressionStack.Peek() is CodeClass)
            {
                CodeClass codeClass = expressionStack.Peek() as CodeClass;
                codeClass.Iface = iface;
            }
            if (expressionStack.Count > 0 && expressionStack.Peek() is CodeInterface)
            {
                CodeInterface codeInterface = expressionStack.Peek() as CodeInterface;
                codeInterface.Iface = iface;
            }
        }

        internal void PushCodeInterface(LexValue value, LexLocation begin, LexLocation end)
        {
            WriteTrace("Creating Inteface " + value.str, "Parser");
            CodeInterface codeInterface = new CodeInterface(value.str, begin, end);
            if (expressionStack.Count > 0)
            {
                while (expressionStack.Count > 0 && expressionStack.Peek() is CodeMember && expressionStack.Peek().IsInScope(codeInterface))
                {
                    if (expressionStack.Peek() is CodeFunction)
                    {
                        CodeFunction function = (CodeFunction)expressionStack.Pop();
                        if (function.IsAbstract)
                            codeInterface.Members.Add(function);
                        else
                            CallHdlr("The function should not have a body", function.Begin);
                    }
                    else
                        CallHdlr("An interface should only declare absctract functions", expressionStack.Pop().Begin);
                }
            }
            expressionStack.Push(codeInterface);
        }
        internal void PushCodeFunction(LexValue value, bool isAbstract, LexLocation begin, LexLocation end)
        {
            if (isAbstract)
                WriteTrace("Creating abstract Function " + value.str, "Parser");
            else
                WriteTrace("Creating Function " + value.str, "Parser");
            CodeFunction codeFunction = new CodeFunction(value.str, isAbstract, begin, end);
            if (expressionStack.Count > 0 && !isAbstract)
            {
                if (expressionStack.Peek() is CodeStatementCollection)
                    codeFunction.Statements = (CodeStatementCollection)expressionStack.Pop();
                else if (expressionStack.Peek() is CodeStatement)
                    codeFunction.Statements.Add((CodeStatement)expressionStack.Pop());
            }
            expressionStack.Push(codeFunction);
        }

        internal void PushCodeStatement(LexValue value, LexLocation begin, LexLocation end)
        {
            CodeStatement statement = new CodeStatement(begin, end);
            PushCodeStatement(statement);
        }

        internal void PushAssignStatement(LexLocation begin, LexLocation end)
        {
            CodeExpression left = expressionStack.Pop();
            CodeExpression right = expressionStack.Pop();
            CodeAssignStatement assignation = new CodeAssignStatement(left, right, begin, end);
            PushCodeStatement(assignation);
        }

        internal void PushConditionExpression(LexLocation begin, LexLocation end, CodeConditionOperator op)
        {
            CodeExpression left = null;
            CodeExpression right = null;
            if (expressionStack.Count > 0)
                left = expressionStack.Pop();
            if (expressionStack.Count > 0)
                right = expressionStack.Pop();
            if (left != null)
            {
                CodeConditionExpression condition;
                if (right != null)
                    condition = new CodeConditionExpression(left, right, op, begin, end);
                else
                    condition = new CodeConditionExpression(left, begin, end);
                expressionStack.Push(condition);
            }
            else
                CallHdlr("Left condition side missing", begin);
        }

        internal void PushConditionExpression(LexLocation begin, LexLocation end)
        {
            CodeExpression left = null;
            CodeExpression right = null;
            if (expressionStack.Count > 0)
                left = expressionStack.Pop();
            if (expressionStack.Count > 0)
                right = expressionStack.Pop();
            if (left != null)
            {
                CodeConditionExpression condition;
                if (right != null)
                    condition = new CodeConditionExpression(left, right, begin, end);
                else
                    condition = new CodeConditionExpression(left, begin, end);
                expressionStack.Push(condition);
            }
            else
                CallHdlr("Left condition side missing", begin);
        }

        internal void PushNotCondition(LexLocation begin, LexLocation end)
        {
            if (expressionStack.Count > 0 && expressionStack.Peek() is CodeConditionExpression)
            {
                CodeConditionExpression notCondition = new CodeConditionExpression(expressionStack.Pop(), new CodeConstant("false", begin, begin), begin, end);
                expressionStack.Push(notCondition);
            }
        }

        internal void PushCallStatement(string name, bool builtInFunction, LexLocation begin, LexLocation end)
        {
            if (expressionStack.Count > 0 && !builtInFunction)
                expressionStack.Push(new CodeCallStatement(name, expressionStack.Pop(), begin, end));
            else
            {
                if (builtInFunction)
                    expressionStack.Push(new CodeCallStatement(name, null, begin, end));
                else
                    CallHdlr("Unknown function", begin);
            }
        }

        internal void PushIndexerStatement(LexLocation begin, LexLocation end)
        {
            PushCallStatement("[]", false, begin, end);
        }

        internal void PushForStatement(LexLocation begin, LexLocation end)
        {

        }

        internal void PushIfStatement(LexLocation begin, LexLocation end)
        {
        }

        internal void PushElseExpression(LexLocation begin, LexLocation end)
        {
        }

        internal void PushAccessExpression(LexValue field, LexLocation begin, LexLocation end)
        {
            CodeExpression variable = expressionStack.Pop();
            CodeAccessExpression access = new CodeAccessExpression(variable, new CodeField(field.str, end, end), begin, end);
            expressionStack.Push(access);
        }

        internal void PushClassAccessExpression(LexValue field, LexLocation begin, LexLocation end)
        {
            CodeExpression variable = expressionStack.Pop();
            CodeClassReference classReference = null;
            if (variable is CodeClassReference)
                classReference = variable as CodeClassReference;
            if (variable is CodeConstant)
                classReference = new CodeClassReference(((CodeConstant)variable).Name, variable.Begin, variable.End);
            CodeAccessExpression access = new CodeClassAccessExpression(classReference, new CodeField(field.str, end, end), begin, end);
            expressionStack.Push(access);

        }

        internal void PushVariable(LexValue variableName, LexLocation location)
        {
            if (variableName.str != null)
            {
                if (variableName.str.Length > 1)
                    variableName.str = variableName.str.Substring(1);
                PushVariable(variableName.str, location);
            }
            else
                CallHdlr("Variable could not be parsed", location);
        }

        internal void PushVariable(string variableName, LexLocation location)
        {
            CodeVariable variable = new CodeVariable(variableName, location, location);
            expressionStack.Push(variable);
        }

        internal void PushThis(LexLocation location)
        {
            PushVariable("this", location);
        }

        internal void PushConstant(LexValue constantName, LexLocation location)
        {
            CodeConstant constant = new CodeConstant(constantName.str, location);
            expressionStack.Push(constant);
        }

        internal void PushVariableDeclarationStatement(LexLocation begin, LexLocation end, bool asGlobal)
        {
            if (expressionStack.Count > 0)
            {
                CodeVariable variable = (CodeVariable)expressionStack.Pop();
                CodeVariableDeclarationStatement declaration = new CodeVariableDeclarationStatement(variable.Name, asGlobal, begin, end);
                PushCodeStatement(declaration);
            }
        }

        internal void UpdateVisibility(LexValue visibility, LexLocation begin, LexLocation end)
        {
            if (!string.IsNullOrEmpty(visibility.str))
            {
                if (expressionStack.Count > 0 && expressionStack.Peek() is CodeMember)
                {
                    CodeMember member = expressionStack.Peek() as CodeMember;
                    if (((Babel.Lexer.Scanner)scanner).GetIdToken(visibility.str) == (int)Tokens.KWPUBLIC || ((Babel.Lexer.Scanner)scanner).GetIdToken(visibility.str) == (int)Tokens.KWVAR)
                        member.Visibility = dragonangel.phpProject.GlyphVisibility.Public;
                    if (((Babel.Lexer.Scanner)scanner).GetIdToken(visibility.str) == (int)Tokens.KWPROTECTED)
                        member.Visibility = dragonangel.phpProject.GlyphVisibility.Protected;
                    if (((Babel.Lexer.Scanner)scanner).GetIdToken(visibility.str) == (int)Tokens.KWPRIVATE)
                        member.Visibility = dragonangel.phpProject.GlyphVisibility.Private;
                }
            }
        }

        internal void PushWhileStatement(LexLocation begin, LexLocation end)
        {
            if (expressionStack.Count > 0 && expressionStack.Peek() is CodeConditionExpression)
            {
                CodeConditionExpression condition = (CodeConditionExpression)expressionStack.Pop();
                CodeWhileStatement whil = new CodeWhileStatement(condition, begin, end);
                PushCodeStatement(whil);
            }

        }

        internal void PushReturnStatement(LexLocation begin, LexLocation end)
        {
            if (expressionStack.Count > 0)
            {
                CodeReturnStatement returnStatement = new CodeReturnStatement(expressionStack.Pop(), begin, end);
                PushCodeStatement(returnStatement);
            }
        }

        internal void PushCodeStatement(CodeStatement statement)
        {
            WriteTrace("Creating Statement " + statement.GetType().Name, "Parser");
            if (expressionStack.Count > 0 && expressionStack.Peek() is CodeStatement)
            {
                CodeStatementCollection statements = new CodeStatementCollection();
                statements.Add((CodeStatement)expressionStack.Pop());
                statements.Add(statement);
                expressionStack.Push(statements);
            }
            if (expressionStack.Count > 0 && expressionStack.Peek() is CodeStatementCollection)
            {
                CodeStatementCollection statements = (CodeStatementCollection)expressionStack.Peek();
                statements.Add(statement);
            }
            else
                expressionStack.Push(statement);
        }

        internal void PushCodeFile()
        {
            WriteTrace("Creating File", "Parser");
            while (expressionStack.Count > 0)
                codeFile.Expressions.Add(expressionStack.Pop());
        }

        internal void WriteTrace(string toTrace, string category)
        {
            if (Trace)
                System.Diagnostics.Trace.WriteLine(toTrace, category);
        }
    }
}
